using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Script.Serialization;
using Nebulosa.Data.Orm;


namespace Nebulosa.Data.Hal {
	public class HalCore {
		protected string path;
		protected Dictionary<string, List<dynamic>> entities;

		
		public HalCore() {
			this.path = HttpContext.Current.Server.MapPath("~/App_Data/nebulosa.ndb");
			this.entities = new Dictionary<string, List<dynamic>>();
			this.LoadEntitiesDatabase();
		}
		
		protected void LoadEntitiesDatabase() {
			if(!File.Exists(this.path)) {
				return;
			}
			
			var dbcontent = File.ReadAllText(this.path);
			
			Nebulosa.Data.Providers.DbProvider.Trace += "#####" + dbcontent + "#####";
			
			JavaScriptSerializer serializer = new JavaScriptSerializer();
			
			Dictionary<string, string> fileDictionary = serializer.Deserialize<Dictionary<string, string>>(dbcontent);
			
			foreach(string key in fileDictionary.Keys) {
				Type keyType = Type.GetType(key);
				
				var listType = typeof(List<>);
				var genericListType = listType.MakeGenericType(keyType);
				
				MethodInfo genericMethod = serializer.GetType().GetMethod("Deserialize", new Type[] { typeof(string) }).MakeGenericMethod(new Type[] { genericListType });
				//this.Cast<List<dynamic>>(genericMethod.Invoke(serializer, new object[] {fileDictionary[key]}));
				
				List<object> listEntities = ((IEnumerable<object>)genericMethod.Invoke(serializer, new object[] {fileDictionary[key]})).Cast<object>().ToList();
				
				this.entities.Add(key, listEntities);
			}
		}
		
		public void Save() {
			Dictionary<string, string> fileDictionary = new Dictionary<string, string>();
			JavaScriptSerializer serializer = new JavaScriptSerializer();
			
			foreach(string key in this.entities.Keys) {
				fileDictionary.Add(key, serializer.Serialize(this.entities[key]));
			}
			
			File.WriteAllText(this.path, serializer.Serialize(fileDictionary));
		}
		
		
		public void Insert<T>(T model) {
			if(!this.entities.ContainsKey(model.GetType().ToString())) {
				this.entities.Add(model.GetType().ToString(), new List<dynamic>());
			}
			
			List<PropertyInfo> primaryKeys = this.Cast<Model>(model).GetPrimaryKeys();
			List<PropertyInfo> indexes = this.Cast<Model>(model).GetIndexes(true);
			
			Type modelType = typeof(T);
			ConstructorInfo ctor = modelType.GetConstructor(new Type[0]);
			T instance = this.Cast<T>(ctor.Invoke(new object[0]));
			
			for(int i = 0; i < primaryKeys.Count; i++) {
				instance.GetType().GetProperty(primaryKeys[i].Name).SetValue(instance, primaryKeys[i].GetValue(model, null), null);
			}
			
			if(this.Get<T>(instance) != null) {
				//throw new Exception("HAL Insert error");
			} else {
				this.entities[typeof(T).ToString()].Add(model);
			}
			
			//si hay pk buscamos por pk
			//si no hay pk, buscamos por indice unico
			
			//si no hay nada que haga de constraint, añadimos
			
			
		}
		
		
		public void Update<T>(T model) {
		
		}
		
		public void Delete<T>(T model) {
		
		}
		
		public Model Get<T>(T model) {
			if(!this.entities.ContainsKey(model.GetType().ToString())) {
				this.entities.Add(model.GetType().ToString(), new List<dynamic>());
			}
			
			List<Model> models = new List<Model>();
			PropertyInfo[] properties = model.GetType().GetProperties();
			
			for(int i = 0; i < this.entities[model.GetType().ToString()].Count; i++) {
				Model loopModel = (Model)this.entities[model.GetType().ToString()][i];
				bool flagFound = false;
				bool flagEquals = true;
				
				for(int j = 0; j < properties.Length; j++) {
					if(properties[j].GetValue(model, null) != null) {
						Nebulosa.Data.Providers.DbProvider.Trace += properties[j].Name + " - " + loopModel.GetType().GetProperty(properties[j].Name) + " - " + new JavaScriptSerializer().Serialize(this.entities);
						
						Nebulosa.Data.Providers.DbProvider.Trace += "#####" + properties[j].GetValue(model, null) + " == " + loopModel.GetType().GetProperty(properties[j].Name).GetValue(loopModel, null) + "####";
				
						if(object.Equals(properties[j].GetValue(model, null), loopModel.GetType().GetProperty(properties[j].Name).GetValue(loopModel, null))) {
							flagFound = true;
							Nebulosa.Data.Providers.DbProvider.Trace += "TRUE_____";
						} else {
							flagEquals = false;
							Nebulosa.Data.Providers.DbProvider.Trace += "FALSE_____";
						}
					}
				}
				
				if(flagFound && flagEquals) {
					models.Add(loopModel);
				}
			}
			
			Nebulosa.Data.Providers.DbProvider.Trace += "#####" + models.Count + "####";
			
			if(models.Count > 0) {
				return models[0];
			} else {
				return null;
			}
		}
		
		protected T Cast<T>(object o) {
			return (T)o;
		}
	}
}